/*
 * Array.java - ScriptME
 * 
 * Copyright (c) 2009 Cesar Henriques <cesar at alttab.com.ar>.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser Public License v2.1
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * 
 * Based on FESI Project
 * 
 * Contributors:
 * 	Jean-Marc Lugrin - initial API and implementation
 * 	Cesar Henriques <cesar at alttab.com.ar> - J2ME Porting and Extensions
 */
package org.scriptme.util;

public final class Array {

	private Array() {
	}

	/**
	 * Creates a new single-dimensioned array.
	 * 
	 * @param componentType
	 *            the type of the array to create
	 * @param length
	 *            the length of the array to create
	 * 
	 * @return the created array, cast to an Object
	 * 
	 * @throws NullPointerException
	 *             if <code>componentType</code> is null
	 * @throws IllegalArgumentException
	 *             if <code>componentType</code> is <code>Void.TYPE</code>
	 * @throws NegativeArraySizeException
	 *             when length is less than 0
	 * @throws OutOfMemoryError
	 *             if memory allocation fails
	 */
	public static Object newInstance(Class componentType, int length) {
		/*
		 * if (! componentType.isPrimitive()) return
		 * createObjectArray(componentType, length);
		 */

		if (componentType == Boolean.class)
			return new boolean[length];
		if (componentType == Byte.class)
			return new byte[length];
		if (componentType == Character.class)
			return new char[length];
		if (componentType == Short.class)
			return new short[length];
		if (componentType == Integer.class)
			return new int[length];
		if (componentType == Long.class)
			return new long[length];
		if (componentType == Float.class)
			return new float[length];
		if (componentType == Double.class)
			return new double[length];
		if (componentType == String.class)
			return new String[length];

		// assert componentType == void.class
		throw new IllegalArgumentException();
	}

	/**
	 * Gets the array length.
	 * 
	 * @param array
	 *            the array
	 * 
	 * @return the length of the array
	 * 
	 * @throws IllegalArgumentException
	 *             if <code>array</code> is not an array
	 * @throws NullPointerException
	 *             if <code>array</code> is null
	 */
	public static int getLength(Object array) {
		if (array instanceof Object[])
			return ((Object[]) array).length;
		if (array instanceof boolean[])
			return ((boolean[]) array).length;
		if (array instanceof byte[])
			return ((byte[]) array).length;
		if (array instanceof char[])
			return ((char[]) array).length;
		if (array instanceof short[])
			return ((short[]) array).length;
		if (array instanceof int[])
			return ((int[]) array).length;
		if (array instanceof long[])
			return ((long[]) array).length;
		if (array instanceof float[])
			return ((float[]) array).length;
		if (array instanceof double[])
			return ((double[]) array).length;
		if (array == null)
			throw new NullPointerException();
		throw new IllegalArgumentException();
	}

	/**
	 * Gets an element of an array. Primitive elements will be wrapped in the
	 * corresponding class type.
	 * 
	 * @param array
	 *            the array to access
	 * @param index
	 *            the array index to access
	 * 
	 * @return the element at <code>array[index]</code>
	 * 
	 * @throws IllegalArgumentException
	 *             if <code>array</code> is not an array
	 * @throws NullPointerException
	 *             if <code>array</code> is null
	 * @throws ArrayIndexOutOfBoundsException
	 *             if <code>index</code> is out of bounds
	 * 
	 * @see #getBoolean(Object, int)
	 * @see #getByte(Object, int)
	 * @see #getChar(Object, int)
	 * @see #getShort(Object, int)
	 * @see #getInt(Object, int)
	 * @see #getLong(Object, int)
	 * @see #getFloat(Object, int)
	 * @see #getDouble(Object, int)
	 */
	public static Object get(Object array, int index) {
		if (array instanceof Object[])
			return ((Object[]) array)[index];
		if (array instanceof boolean[])
			return ((boolean[]) array)[index] ? Boolean.TRUE : Boolean.FALSE;
		if (array instanceof byte[])
			return new Byte(((byte[]) array)[index]);
		if (array instanceof char[])
			return new Character(((char[]) array)[index]);
		if (array instanceof short[])
			return new Short(((short[]) array)[index]);
		if (array instanceof int[])
			return new Integer(((int[]) array)[index]);
		if (array instanceof long[])
			return new Long(((long[]) array)[index]);
		if (array instanceof float[])
			return new Float(((float[]) array)[index]);
		if (array instanceof double[])
			return new Double(((double[]) array)[index]);
		if (array == null)
			throw new NullPointerException();
		throw new IllegalArgumentException();
	}

	/**
	 * Gets an element of a boolean array.
	 * 
	 * @param array
	 *            the array to access
	 * @param index
	 *            the array index to access
	 * 
	 * @return the boolean element at <code>array[index]</code>
	 * 
	 * @throws IllegalArgumentException
	 *             if <code>array</code> is not a boolean array
	 * @throws NullPointerException
	 *             if <code>array</code> is null
	 * @throws ArrayIndexOutOfBoundsException
	 *             if <code>index</code> is out of bounds
	 * 
	 * @see #get(Object, int)
	 */
	public static boolean getBoolean(Object array, int index) {
		if (array instanceof boolean[])
			return ((boolean[]) array)[index];
		if (array == null)
			throw new NullPointerException();
		throw new IllegalArgumentException();
	}

	/**
	 * Gets an element of a byte array.
	 * 
	 * @param array
	 *            the array to access
	 * @param index
	 *            the array index to access
	 * 
	 * @return the byte element at <code>array[index]</code>
	 * 
	 * @throws IllegalArgumentException
	 *             if <code>array</code> is not a byte array
	 * @throws NullPointerException
	 *             if <code>array</code> is null
	 * @throws ArrayIndexOutOfBoundsException
	 *             if <code>index</code> is out of bounds
	 * 
	 * @see #get(Object, int)
	 */
	public static byte getByte(Object array, int index) {
		if (array instanceof byte[])
			return ((byte[]) array)[index];
		if (array == null)
			throw new NullPointerException();
		throw new IllegalArgumentException();
	}

	/**
	 * Gets an element of a char array.
	 * 
	 * @param array
	 *            the array to access
	 * @param index
	 *            the array index to access
	 * 
	 * @return the char element at <code>array[index]</code>
	 * 
	 * @throws IllegalArgumentException
	 *             if <code>array</code> is not a char array
	 * @throws NullPointerException
	 *             if <code>array</code> is null
	 * @throws ArrayIndexOutOfBoundsException
	 *             if <code>index</code> is out of bounds
	 * 
	 * @see #get(Object, int)
	 */
	public static char getChar(Object array, int index) {
		if (array instanceof char[])
			return ((char[]) array)[index];
		if (array == null)
			throw new NullPointerException();
		throw new IllegalArgumentException();
	}

	/**
	 * Gets an element of a short array.
	 * 
	 * @param array
	 *            the array to access
	 * @param index
	 *            the array index to access
	 * 
	 * @return the short element at <code>array[index]</code>
	 * 
	 * @throws IllegalArgumentException
	 *             if <code>array</code> is not a byte or char array
	 * @throws NullPointerException
	 *             if <code>array</code> is null
	 * @throws ArrayIndexOutOfBoundsException
	 *             if <code>index</code> is out of bounds
	 * 
	 * @see #get(Object, int)
	 */
	public static short getShort(Object array, int index) {
		if (array instanceof short[])
			return ((short[]) array)[index];
		return getByte(array, index);
	}

	/**
	 * Gets an element of an int array.
	 * 
	 * @param array
	 *            the array to access
	 * @param index
	 *            the array index to access
	 * 
	 * @return the int element at <code>array[index]</code>
	 * 
	 * @throws IllegalArgumentException
	 *             if <code>array</code> is not a byte, char, short, or int
	 *             array
	 * @throws NullPointerException
	 *             if <code>array</code> is null
	 * @throws ArrayIndexOutOfBoundsException
	 *             if <code>index</code> is out of bounds
	 * 
	 * @see #get(Object, int)
	 */
	public static int getInt(Object array, int index) {
		if (array instanceof int[])
			return ((int[]) array)[index];
		if (array instanceof char[])
			return ((char[]) array)[index];
		return getShort(array, index);
	}

	/**
	 * Gets an element of a long array.
	 * 
	 * @param array
	 *            the array to access
	 * @param index
	 *            the array index to access
	 * 
	 * @return the long element at <code>array[index]</code>
	 * 
	 * @throws IllegalArgumentException
	 *             if <code>array</code> is not a byte, char, short, int, or
	 *             long array
	 * @throws NullPointerException
	 *             if <code>array</code> is null
	 * @throws ArrayIndexOutOfBoundsException
	 *             if <code>index</code> is out of bounds
	 * 
	 * @see #get(Object, int)
	 */
	public static long getLong(Object array, int index) {
		if (array instanceof long[])
			return ((long[]) array)[index];
		return getInt(array, index);
	}

	/**
	 * Gets an element of a float array.
	 * 
	 * @param array
	 *            the array to access
	 * @param index
	 *            the array index to access
	 * 
	 * @return the float element at <code>array[index]</code>
	 * 
	 * @throws IllegalArgumentException
	 *             if <code>array</code> is not a byte, char, short, int,
	 *             long, or float array
	 * @throws NullPointerException
	 *             if <code>array</code> is null
	 * @throws ArrayIndexOutOfBoundsException
	 *             if <code>index</code> is out of bounds
	 * 
	 * @see #get(Object, int)
	 */
	public static float getFloat(Object array, int index) {
		if (array instanceof float[])
			return ((float[]) array)[index];
		return getLong(array, index);
	}

	/**
	 * Gets an element of a double array.
	 * 
	 * @param array
	 *            the array to access
	 * @param index
	 *            the array index to access
	 * 
	 * @return the double element at <code>array[index]</code>
	 * 
	 * @throws IllegalArgumentException
	 *             if <code>array</code> is not a byte, char, short, int,
	 *             long, float, or double array
	 * @throws NullPointerException
	 *             if <code>array</code> is null
	 * @throws ArrayIndexOutOfBoundsException
	 *             if <code>index</code> is out of bounds
	 * 
	 * @see #get(Object, int)
	 */
	public static double getDouble(Object array, int index) {
		if (array instanceof double[])
			return ((double[]) array)[index];
		return getFloat(array, index);
	}

	/**
	 * Sets an element of an array. If the array is primitive, then the new
	 * value is unwrapped and widened.
	 * 
	 * @param array
	 *            the array to set a value of
	 * @param index
	 *            the array index to set the value to
	 * @param value
	 *            the value to set
	 * 
	 * @throws IllegalArgumentException
	 *             if <code>array</code> is not an array, or the array is
	 *             primitive and unwrapping value fails, or the value is not
	 *             assignable to the array component type
	 * @throws NullPointerException
	 *             if array is null, or if array is primitive and value is null
	 * @throws ArrayIndexOutOfBoundsException
	 *             if <code>index</code> is out of bounds
	 * 
	 * @see #setBoolean(Object, int, boolean)
	 * @see #setByte(Object, int, byte)
	 * @see #setChar(Object, int, char)
	 * @see #setShort(Object, int, short)
	 * @see #setInt(Object, int, int)
	 * @see #setLong(Object, int, long)
	 * @see #setFloat(Object, int, float)
	 * @see #setDouble(Object, int, double)
	 */
	public static void set(Object array, int index, Object value) {
		if (array instanceof Object[]) {
			((Object[]) array)[index] = value;
		} else if (value instanceof Boolean)
			setBoolean(array, index, ((Boolean) value).booleanValue());
		else if (value instanceof Byte)
			setByte(array, index, ((Byte) value).byteValue());
		else if (value instanceof Character)
			setChar(array, index, ((Character) value).charValue());
		else if (value instanceof Short)
			setShort(array, index, ((Short) value).shortValue());
		else if (value instanceof Integer)
			setInt(array, index, ((Integer) value).intValue());
		else if (value instanceof Long)
			setLong(array, index, ((Long) value).longValue());
		else if (value instanceof Float)
			setFloat(array, index, ((Float) value).floatValue());
		else if (value instanceof Double)
			setDouble(array, index, ((Double) value).doubleValue());
		else if (array == null)
			throw new NullPointerException();
		else
			throw new IllegalArgumentException();
	}

	/**
	 * Sets an element of a boolean array.
	 * 
	 * @param array
	 *            the array to set a value of
	 * @param index
	 *            the array index to set the value to
	 * @param value
	 *            the value to set
	 * 
	 * @throws IllegalArgumentException
	 *             if <code>array</code> is not a boolean array
	 * @throws NullPointerException
	 *             if <code>array</code> is null
	 * @throws ArrayIndexOutOfBoundsException
	 *             if <code>index</code> is out of bounds
	 * 
	 * @see #set(Object, int, Object)
	 */
	public static void setBoolean(Object array, int index, boolean value) {
		if (array instanceof boolean[])
			((boolean[]) array)[index] = value;
		else if (array == null)
			throw new NullPointerException();
		else
			throw new IllegalArgumentException();
	}

	/**
	 * Sets an element of a byte array.
	 * 
	 * @param array
	 *            the array to set a value of
	 * @param index
	 *            the array index to set the value to
	 * @param value
	 *            the value to set
	 * 
	 * @throws IllegalArgumentException
	 *             if <code>array</code> is not a byte, short, int, long,
	 *             float, or double array
	 * @throws NullPointerException
	 *             if <code>array</code> is null
	 * @throws ArrayIndexOutOfBoundsException
	 *             if <code>index</code> is out of bounds
	 * 
	 * @see #set(Object, int, Object)
	 */
	public static void setByte(Object array, int index, byte value) {
		if (array instanceof byte[])
			((byte[]) array)[index] = value;
		else
			setShort(array, index, value);
	}

	/**
	 * Sets an element of a char array.
	 * 
	 * @param array
	 *            the array to set a value of
	 * @param index
	 *            the array index to set the value to
	 * @param value
	 *            the value to set
	 * 
	 * @throws IllegalArgumentException
	 *             if <code>array</code> is not a char, int, long, float, or
	 *             double array
	 * @throws NullPointerException
	 *             if <code>array</code> is null
	 * @throws ArrayIndexOutOfBoundsException
	 *             if <code>index</code> is out of bounds
	 * 
	 * @see #set(Object, int, Object)
	 */
	public static void setChar(Object array, int index, char value) {
		if (array instanceof char[])
			((char[]) array)[index] = value;
		else
			setInt(array, index, value);
	}

	/**
	 * Sets an element of a short array.
	 * 
	 * @param array
	 *            the array to set a value of
	 * @param index
	 *            the array index to set the value to
	 * @param value
	 *            the value to set
	 * 
	 * @throws IllegalArgumentException
	 *             if <code>array</code> is not a short, int, long, float, or
	 *             double array
	 * @throws NullPointerException
	 *             if <code>array</code> is null
	 * @throws ArrayIndexOutOfBoundsException
	 *             if <code>index</code> is out of bounds
	 * 
	 * @see #set(Object, int, Object)
	 */
	public static void setShort(Object array, int index, short value) {
		if (array instanceof short[])
			((short[]) array)[index] = value;
		else
			setInt(array, index, value);
	}

	/**
	 * Sets an element of an int array.
	 * 
	 * @param array
	 *            the array to set a value of
	 * @param index
	 *            the array index to set the value to
	 * @param value
	 *            the value to set
	 * 
	 * @throws IllegalArgumentException
	 *             if <code>array</code> is not an int, long, float, or double
	 *             array
	 * @throws NullPointerException
	 *             if <code>array</code> is null
	 * @throws ArrayIndexOutOfBoundsException
	 *             if <code>index</code> is out of bounds
	 * 
	 * @see #set(Object, int, Object)
	 */
	public static void setInt(Object array, int index, int value) {
		if (array instanceof int[])
			((int[]) array)[index] = value;
		else
			setLong(array, index, value);
	}

	/**
	 * Sets an element of a long array.
	 * 
	 * @param array
	 *            the array to set a value of
	 * @param index
	 *            the array index to set the value to
	 * @param value
	 *            the value to set
	 * 
	 * @throws IllegalArgumentException
	 *             if <code>array</code> is not a long, float, or double array
	 * @throws NullPointerException
	 *             if <code>array</code> is null
	 * @throws ArrayIndexOutOfBoundsException
	 *             if <code>index</code> is out of bounds
	 * 
	 * @see #set(Object, int, Object)
	 */
	public static void setLong(Object array, int index, long value) {
		if (array instanceof long[])
			((long[]) array)[index] = value;
		else
			setFloat(array, index, value);
	}

	/**
	 * Sets an element of a float array.
	 * 
	 * @param array
	 *            the array to set a value of
	 * @param index
	 *            the array index to set the value to
	 * @param value
	 *            the value to set
	 * 
	 * @throws IllegalArgumentException
	 *             if <code>array</code> is not a float or double array
	 * @throws NullPointerException
	 *             if <code>array</code> is null
	 * @throws ArrayIndexOutOfBoundsException
	 *             if <code>index</code> is out of bounds
	 * 
	 * @see #set(Object, int, Object)
	 */
	public static void setFloat(Object array, int index, float value) {
		if (array instanceof float[])
			((float[]) array)[index] = value;
		else
			setDouble(array, index, value);
	}

	/**
	 * Sets an element of a double array.
	 * 
	 * @param array
	 *            the array to set a value of
	 * @param index
	 *            the array index to set the value to
	 * @param value
	 *            the value to set
	 * 
	 * @throws IllegalArgumentException
	 *             if <code>array</code> is not a double array
	 * @throws NullPointerException
	 *             if <code>array</code> is null
	 * @throws ArrayIndexOutOfBoundsException
	 *             if <code>index</code> is out of bounds
	 * 
	 * @see #set(Object, int, Object)
	 */
	public static void setDouble(Object array, int index, double value) {
		if (array instanceof double[])
			((double[]) array)[index] = value;
		else if (array == null)
			throw new NullPointerException();
		else
			throw new IllegalArgumentException();
	}
}
